package com.ihr360.job.core.listener;

import com.ihr360.job.core.ExitStatus;
import com.ihr360.job.core.support.MethodInvoker;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;

import java.util.Map;
import java.util.Set;

/**
 * {@link MethodInterceptor} that, given a map of method names and
 * {@link MethodInvoker}s, will execute all methods tied to a particular method
 * name, with the provided arguments. The only possible return value that is
 * handled is of type ExitStatus, since the only StepListener implementation
 * that isn't void is
 * {@link StepExecutionListener#afterStep(com.ihr360.job.core.StepExecution)}
 * , which returns ExitStatus.
 *
 * @author Lucas Ward
 * @since 2.0
 * @see MethodInvoker
 */
public class MethodInvokerMethodInterceptor implements MethodInterceptor {
    private final Map<String, Set<MethodInvoker>> invokerMap;
    private final boolean ordered;

    public MethodInvokerMethodInterceptor(Map<String, Set<MethodInvoker>> invokerMap) {
        this(invokerMap, false);
    }

    public MethodInvokerMethodInterceptor(Map<String, Set<MethodInvoker>> invokerMap, boolean ordered) {
        this.ordered = ordered;
        this.invokerMap = invokerMap;
    }

    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {

        String methodName = invocation.getMethod().getName();
        if (ordered && methodName.equals("getOrder")) {
            return invocation.proceed();
        }

        Set<MethodInvoker> invokers = invokerMap.get(methodName);

        if (invokers == null) {
            return null;
        }
        ExitStatus status = null;
        for (MethodInvoker invoker : invokers) {
            Object retVal = invoker.invokeMethod(invocation.getArguments());
            if (retVal instanceof ExitStatus) {
                if (status != null) {
                    status = status.and((ExitStatus) retVal);
                }
                else {
                    status = (ExitStatus) retVal;
                }
            }
        }

        // The only possible return values are ExitStatus or int (from Ordered)
        return status;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public boolean equals(Object obj) {
        if (!(obj instanceof MethodInvokerMethodInterceptor)) {
            return false;
        }
        MethodInvokerMethodInterceptor other = (MethodInvokerMethodInterceptor) obj;
        return invokerMap.equals(other.invokerMap);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public int hashCode() {
        return invokerMap.hashCode();
    }
}